/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package genetic;

import generateCLP.CLPWriter;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Observable;
import java.util.logging.Level;
import java.util.logging.Logger;
import reader.Data;
import reader.Filereader;
import reader.Header;

/**
 *
 * @author Ifun
 */
public class GeneticAlgorithm extends Observable implements Runnable {
    // untuk thread dan update view
    String log = "";
    boolean stop = false;
    int stateGA = 0;
    
    Individual bestIndividu = new Individual();
    int treshold;
    float fraksiCross;
    Population population;
    int maxFitness;
    
    
    public GeneticAlgorithm() {}
    
    public Population getPopulation() {return this.population;}
    public Individual getBestIndividu() {return this.bestIndividu;}
    public int getState() {return this.stateGA;}
    
    public static void main(String[] args) throws Exception {
            Filereader fr = new Filereader("dataset\\nursery - 6000an.arff");
            //Filereader fr = new Filereader("dataset\\nursery.arff");
            fr.doRead();
            Data data = fr.getData();

            Individual best = new Individual();
            GeneticAlgorithm ga = new GeneticAlgorithm();

            best = ga.GeneticAlgorithm(10, 46, 10, 80, 1, fr.getHeader(), data);
         
    }
        
//    treshold,chromosome perindividu, individu per populasi, fraksi, mutasi
    public Individual GeneticAlgorithm(int pTreshold, int nChrom, int nIndividu, int pCrossover, int rateMutation, Header atrib, Data data) throws Exception
    {
        Individual bestIndividu = new Individual();
        
        // initialize parameter
        int treshold = Math.round(data.getJumlahData() * pTreshold / 100);
        float fraksiCross = (float) pCrossover/100;
        System.out.println(fraksiCross);
        log.concat("Inisialisasi parameter.");
        
        // Initialize population
        Population population = new Population(nIndividu, nChrom, atrib);
        log.concat("Inisialisasi parameter.");
        
        // evaluate fitness
        population.updateFitness(data);
        log.concat("Inisialisasi parameter.");
        
        // cek max fitness
        int maxFitness = population.getMaxFitness();
        System.out.println(maxFitness + " , " + treshold);
        log.concat("Inisialisasi parameter.");
        while(maxFitness<treshold)
        {
            // initialize new generation
            Population newGeneration = new Population();
            
            // selection
            // calculate/update probability
            population.updateProbability();
            log.concat("Inisialisasi parameter.");
            // rank population based on probability - descending
            population.rankPopulation();
            ArrayList<Individual> selectedIndividu = population.select(fraksiCross);
            for(int i = 0; i<selectedIndividu.size();++i)
            {
                newGeneration.getSociety().add(selectedIndividu.get(i));
            }
            
            //crossover
            // set pair
            ArrayList<Point> pairs = population.pairsIndividual(fraksiCross);
            // create offspring based on pairs
            for(int i=0;i<pairs.size();++i)
            {
                Individual[] childs = new Individual[2];
                childs[0] = new Individual();
                childs[1] = new Individual();
                childs = population.getSociety().get(pairs.get(i).x).createChilds(population.getSociety().get(pairs.get(i).y));
                newGeneration.getSociety().add(childs[0]);
                newGeneration.getSociety().add(childs[1]);
            }
            
            // mutation
            int mutate = newGeneration.mutationPop(rateMutation);
            
            // update fitness
            newGeneration.updateFitness(data);
            System.out.println(population.getSociety().size() + " , " + newGeneration.getSociety().size());
            maxFitness = newGeneration.getMaxFitness();
            // set population = new generation
            population.getSociety().clear();
            for(int i=0;i<newGeneration.getSociety().size();++i)
            {
                population.getSociety().add(newGeneration.getSociety().get(i));
            }
            newGeneration.getSociety().clear();
            System.out.println(maxFitness + " , " + treshold);
        }
        
        population.rankPopulation();
        bestIndividu = population.getSociety().get(0);
        
        return bestIndividu;
    }
    
    
    int pTreshold;
    int nChrom;
    int nIndividu;
    int pCrossover;
    int rateMutation;
    Header atrib;
    Data data;
    //    treshold,chromosome perindividu, individu per populasi, fraksi, mutasi
    public void inisialisasiGeneticAlgorithm(int pTreshold, int nChrom, int nIndividu, int pCrossover, int rateMutation, Header atrib, Data data) throws Exception
    {
        this.pTreshold=pTreshold;
        this.nChrom=nChrom;
        this.nIndividu=nIndividu;
        this.pCrossover=pCrossover;
        this.rateMutation=rateMutation;
        this.atrib=atrib;
        this.data=data;
    }
    
    public String getLog()
    {
        return log;
    }
    
    public void StopIterate() {
        stop = true;
        stateGA = 2; // wait for iteration to stop
        stateChanged();
    }

    public void run() {
        bestIndividu = new Individual();
        
        // initialize parameter
        treshold = Math.round(data.getJumlahData() * pTreshold / 100);
        
        fraksiCross = (float) pCrossover/100;
//        System.out.println(treshold + "," + fraksiCross + "," + rateMutation + "," + pCrossover + "," + nChrom + "," + nIndividu + "," + data.getJumlahData());
//        System.out.println(pTreshold + "," + pCrossover);
        setLog("GENETIC ALGORITHM.");
        setLog("Inisialisasi parameter.");
        
        // Initialize population
        setLog("Inisialisasi populasi: " + nIndividu + " individu, " + nChrom + " kromosom dimulai...");
        population = new Population(nIndividu, nChrom, atrib);
        try {
            // evaluate fitness
            setLog("Evaluasi fitness individu dimulai...");
            population.updateFitness(data);
        } catch (Exception ex) {
            Logger.getLogger(GeneticAlgorithm.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        // cek max fitness
        maxFitness = population.getMaxFitness();
        setLog("Max Fitness: " + maxFitness + " , Treshold: " + treshold);
        
        int iterateCount = 1;
        while(!stop && maxFitness<treshold)
        {
            try {
                setLog("Iterasi Ke "+iterateCount);
                // initialize new generation
                Population newGeneration = new Population();
                
                // selection
                setLog("Seleksi populasi dimulai...");
                // calculate/update probability
                population.updateProbability();
                //setLog("Inisialisasi parameter.");
                // rank population based on probability - descending
                population.rankPopulation();
                ArrayList<Individual> selectedIndividu = population.select(fraksiCross);
                for(int i = 0; i<selectedIndividu.size();++i)
                {
                    newGeneration.getSociety().add(selectedIndividu.get(i));
                }
                
                //crossover
                // set pair
                setLog("Crossover antara beberapa individu dimulai...");
                //System.out.println("ccccccccccc");
                ArrayList<Point> pairs = population.pairsIndividual(fraksiCross);
                //System.out.println("ddddddddddd");
                // create offspring based on pairs
                for(int i=0;i<pairs.size();++i)
                {
                    Individual[] childs = new Individual[2];
                    childs[0] = new Individual();
                    childs[1] = new Individual();
                    //System.out.println("eeeeeeeeeeeeee");
                    childs = population.getSociety().get(pairs.get(i).x).createChilds(population.getSociety().get(pairs.get(i).y));
                    //System.out.println("fffffffffffff");
                    newGeneration.getSociety().add(childs[0]);
                    newGeneration.getSociety().add(childs[1]);
                }
                // mutation
                int mutate = newGeneration.mutationPop(rateMutation);
                
                if(mutate>-1)
                {
                    setLog("Terjadi mutasi pada 1 individu...");
                } else
                {
                    setLog("Tidak terjadi mutasi pada populasi baru");
                }
                // update fitness
                setLog("Evaluasi Fitness pada populasi baru dimulai...");
                newGeneration.updateFitness(data);
                //setLog(population.getSociety().size() + " , " + newGeneration.getSociety().size());
                maxFitness = newGeneration.getMaxFitness();
                // set population = new generation
                population.getSociety().clear();
                for(int i=0;i<newGeneration.getSociety().size();++i)
                {
                    population.getSociety().add(newGeneration.getSociety().get(i));
                }
                newGeneration.getSociety().clear();
                setLog("Max Fitness: " + maxFitness + " , Treshold: " + treshold);
                iterateCount++;
            } catch (Exception ex) {
                Logger.getLogger(GeneticAlgorithm.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        setLog("SELESAI.");
        population.rankPopulation();
        bestIndividu = population.getSociety().get(0);
        stateGA = 3; // fin
        stateChanged();
    }
    
    public void setLog(String s) {
        stateGA = 1; // add log
        log = s;
        stateChanged();
    }
    
    private void stateChanged() {
        setChanged();
        notifyObservers();
    }

}
